package com.sec.android.app.myfiles.util;

import android.app.Activity;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.widget.Toast;

import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.fragment.AbsMyFilesFragment;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.info.AppConstants.StoragePath;
import com.sec.android.app.myfiles.info.AppFeatures;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.info.MediaFile;
import com.sec.android.app.myfiles.info.MimeType;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.module.abstraction.FileRecord.StorageType;
import com.sec.android.app.myfiles.module.local.category.CategoryFileRecord;
import com.sec.android.app.myfiles.module.local.file.LocalFileRecord;
import com.sec.android.app.myfiles.navigation.NavigationInfo;
import com.sec.android.app.myfiles.operation.FileOperator;
import com.sec.android.app.myfiles.provider.DbTableInfo;
import com.sec.android.app.myfiles.provider.DbTableInfo.COLUMN_ID;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

public class FileUtils {
    private static final String TAG = "FileUtils";
    private static final String DEFAULT_DOWNLOAD_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
    public static final Uri MEDIA_PROVIDER_URI = MediaStore.Files.getContentUri("external");
    public static final String FILE_PROVIDER_AUTHORITY = "com.runboy.private.myfiles.FileProvider";
    private static final Pattern PATTERN_INTERNAL_ROOT_CONVERT = Pattern.compile(StoragePath.INTERNAL_ROOT_CONVERT, Pattern.LITERAL);
    private static final Pattern PATTERN_ROOT_CONVERT = Pattern.compile(StoragePath.ROOT_CONVERT, Pattern.LITERAL);

    private static final String GALLERY_COLUMN_CLOUD_IS_CACHE = "cloud_is_cached";

    public interface ITEM_TYPE {
        int NONE = 0;
        int FILE = 1;
        int FOLDER = 2;
        int FILE_AND_FOLDER = FILE | FOLDER;
        int SHORTCUT = 4;
        int CATEGORY_FOLDER = 8;
    }

    public static final long KILO_BYTES = 1024L;
    public static final long MEGA_BYTES = KILO_BYTES * 1024;
    public static final long GIGA_BYTES = MEGA_BYTES * 1024;
    public static final long TERA_BYTES = GIGA_BYTES * 1024;
    public static final long PETA_BYTES = TERA_BYTES * 1024;

    public static void openGoogleDocsFile(String googleDocsLink, Activity activity) {
        if (activity == null || googleDocsLink == null) {
            Log.e(TAG, "openGoogleDocsFile : activity or alternateLink is null");
            return;
        }

        Uri link = Uri.parse(googleDocsLink);
        final Intent intent = new Intent(Intent.ACTION_VIEW);
        ComponentName component = new ComponentName("com.google.android.apps.docs", "com.google.android.apps.docs.openurl.OpenUrlActivity");
        intent.setComponent(component);
        intent.setData(link);

        try {
            activity.startActivity(intent);
        } catch (Exception e) {
            Log.e(TAG, "openGoogleDocsFile : " + e.getMessage());
            Toast.makeText(activity, R.string.unable_to_find_application, Toast.LENGTH_SHORT).show();
        }
    }

    public static boolean isGoogleDocs(String file_mimeType) {
        if (file_mimeType == null) {
            return false;
        }

        boolean bRet = false;
        String[] mimeTypesOfGoogleDocs = {
                MimeType.GOOGLE_DOCS, MimeType.GOOGLE_SLIDES, MimeType.GOOGLE_SHEETS, MimeType.GOOGLE_DRAWING};

        for (String a : mimeTypesOfGoogleDocs) {
            if (a.equalsIgnoreCase(file_mimeType)) {
                bRet = true;
                break;
            }
        }
        return bRet;
    }

    public static void updateRecentFile(Activity activity, FileRecord record) {
        ContentResolver resolver = activity.getContentResolver();
        DbTableInfo column = DbTableInfo.getInstance(StorageType.Recent);
        ContentValues value = new ContentValues();
        StorageType storageType = record.getStorageType();
        if (column != null
                && (storageType == StorageType.Local || storageType == StorageType.Recent || storageType == StorageType.Downloads)) {
            value.put(column.getColumnName(COLUMN_ID.PATH), record.getPath());
            value.put(column.getColumnName(COLUMN_ID.NAME), record.getName());
            value.put(column.getColumnName(COLUMN_ID.EXT), record.getExt());
            value.put(column.getColumnName(COLUMN_ID.FILE_TYPE), record.getFileType());
            value.put(column.getColumnName(COLUMN_ID.SIZE), record.getSize());
            value.put(column.getColumnName(COLUMN_ID.DATE), record.getDate());
            value.put(column.getColumnName(COLUMN_ID.RECENT_DATE), System.currentTimeMillis());
            value.put(column.getColumnName(COLUMN_ID.IS_360_CONTENTS), record.isGear360Contents());
            Uri uri = Uri.parse(column.getUri());
            resolver.insert(uri, value);
        }
    }

    public static void updateDateAccessed(Activity activity, Uri mediaUri) {
        if (AppFeatures.isSupportDataAccessed(activity.getApplicationContext())) {
            ContentResolver cr = activity.getContentResolver();

            ContentValues values = new ContentValues();
            values.put("date_accessed", System.currentTimeMillis() / 1000);
            try {
                cr.update(mediaUri, values, null, null);
            } catch (Exception e) {
                Log.d(TAG, "updateDateAccessed() ] Exception = " + e.getMessage());
            }
        }
    }

    public static boolean isMediaProviderUri(Uri uri) {
        if (uri == null) {
            return false;
        }

        boolean bRet = false;
        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            if (uri.getUserInfo() != null) {
                String authority = uri.getAuthority();
                int index = authority.lastIndexOf('@');
                authority = authority.substring(index + 1);
                if (AppConstants.MEDIA_PROVIDER_AUTHORITY.equals(authority)) {
                    bRet = true;
                }
            } else if (AppConstants.MEDIA_PROVIDER_AUTHORITY.equals(uri.getAuthority())) {
                bRet = true;
            }
        }
        return bRet;
    }

    public static boolean isPrivateFolder(String folder) {
        return isUnderPath(StoragePath.PRIVATE_ROOT, folder);
    }

    private static final Set<String> mSystemFolderSet;

    static {
        mSystemFolderSet = new HashSet<>();
        mSystemFolderSet.add("/system");
        mSystemFolderSet.add("/sys");
        mSystemFolderSet.add("/proc");
        mSystemFolderSet.add("/storage/sdcard0");
        mSystemFolderSet.add("/storage/emulated/legacy");
        mSystemFolderSet.add("/storage/container");
    }

    public static boolean isSystemFolder(String name) {
        return mSystemFolderSet.contains(name);
    }

    public static Uri getUriByPath(Context context, String path) {
        if (!SemFwWrapper.file(path).exists()) {
            return null;
        }

        Uri uri = getMediaUriByPath(context, path);
        if (uri != null) {
            uri = UiUtils.addUserIdToUri(uri);
        } else {
            uri = getContentUri(context, path);
        }
        return uri;
    }

    public static Uri getPickUriByPath(Context context, String path) {
        File file = SemFwWrapper.file(path);
        if (!file.exists()) {
            return null;
        }

        Uri uri = getMediaUriByPath(context, path);
        if (uri != null) {
            uri = UiUtils.addUserIdToUri(uri);
        } else {
            uri = Uri.fromFile(file);
        }
        return uri;
    }

    private static Uri getMediaUriByPath(Context context, String path) {
        int fileType = MediaFile.getFileTypeForMimeType(MediaFile.getMimeType(context, path));
        Uri uri = null;
        if (!hasMediaScanSkipPath(path)) {
            if (FileType.isAudioFileType(fileType)) {
                uri = getMediaUri(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, path);
            } else if (FileType.isVideoFileType(fileType)) {
                uri = getMediaUri(context, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, path);
            } else if (FileType.isImageFileType(fileType)) {
                uri = getMediaUri(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, path);
            }
        }
        if (uri == null && !SemFwWrapper.file(path).isDirectory()) {// for SideSync
            uri = getMediaUri(context, MEDIA_PROVIDER_URI, path);
        }
        return uri;
    }

    public static long getPhoneMemoryInfo(Context context, int mediaType, boolean isSize) {
        long ret = -1;
        String[] projection = (isSize) ? new String[]{"sum(_size)"} : new String[]{"count(*)"};
        try (Cursor c = context.getContentResolver().query(FileUtils.MEDIA_PROVIDER_URI, projection, "storage_id=65537 AND media_type=" + mediaType, null, null)) {
            if ((c != null) && (c.moveToFirst())) {
                ret = c.getLong(0);
            }
        }
        return ret;
    }

    public static boolean hasMediaScanSkipPath(String path) {
        boolean ret = false;

        for (String skipDirectory : AppConstants.MEDIASCAN_SKIP_DIRECTORY) {
            if (path.contains(skipDirectory)) {
                ret = true;
                break;
            }
        }
        return ret;
    }

    public static Uri getContentUri(Context context, String path) {
        if (path == null) {
            return null;
        }
        Uri ret = null;
        try {
            ret = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, SemFwWrapper.file(path));
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "IllegalArgumentException:" + e.toString());
        }
        return ret;
    }

    public static Uri getMediaUri(Context context, Uri mediaUri, String path) {
        Uri ret = null;
        ContentResolver cr = context.getContentResolver();
        try (Cursor c = cr.query(mediaUri, new String[]{MediaColumns._ID, MediaColumns.DATA},
                MediaColumns.DATA + "= ? COLLATE LOCALIZED",
                new String[]{path}, null)) {
            if (c != null && c.getCount() > 0 && c.moveToFirst() && c.getString(c.getColumnIndex(MediaColumns.DATA)).equals(path)) {
                ret = ContentUris.withAppendedId(mediaUri, c.getLong(c.getColumnIndex(MediaColumns._ID)));
            }
        } catch (SecurityException e) {
            Log.e(TAG, "SecurityException:" + e.toString());
        }
        return ret;
    }

    public static void sendScan(Context context, String dirPath) {
    }

    public static void sendScan(Context context, String[] filePaths, MediaScannerConnection.OnScanCompletedListener callback) {
        if (filePaths.length > 0) {
            Log.d(TAG, "scan directory - " + filePaths[0]);
            SemFwWrapper.semScanDirectories(context, filePaths, callback);
        }
    }

    public static void sendScanSingleFile(Context context, String filePath) {
        String mimeType = MediaFile.getMimeType(context, filePath);
        MediaScannerConnection.scanFile(context, new String[]{filePath}, new String[]{mimeType}, null);
    }

    private static File getUniqueFile(String fileName) {
        if (fileName == null) {
            return null;
        }
        String fileExt = FileRecord.getExt(fileName);
        String fileNameWithoutExt = FileRecord.getName(fileName);
        File file;
        int i = -1;
        do {
            String newFileName;
            if (i >= 0) {
                StringBuilder sb = new StringBuilder(fileNameWithoutExt);
                sb.append(" (");
                sb.append(i);
                sb.append(").");
                sb.append(fileExt);
                newFileName = sb.toString();
            } else {
                newFileName = fileName;
            }
            file = SemFwWrapper.file(DEFAULT_DOWNLOAD_DIR + File.separator + newFileName);
            i++;
        } while (file.exists());
        return file;
    }

    public static void createFileFromUri(Context context, Uri uri, File file) {
        if (file == null) {
            return;
        }
        try (InputStream input = context.getContentResolver().openInputStream(uri);
             OutputStream output = new FileOutputStream(file)) {
            if (input == null) {
                return;
            }
            final byte[] buffer = new byte[1024];
            int read;
            while ((read = input.read(buffer)) != -1) {
                output.write(buffer, 0, read);
            }
            output.flush();
        } catch (IOException e) {
            Log.e(TAG, "IOException:" + e.toString());
        }
    }

    // To use FileProvider uri, local file path is defined in file_paths.xml like below :
    // '/storage/emulated/0' <-> '/device_storage'
    // '/storage' <-> '/external'
    // Thus, to get directory path from FileProvider uri, it has to be converted.
    private static String convertDirPathFromUri(Uri uri) {
        String ret = uri.getPath();
        if (ret.startsWith(StoragePath.INTERNAL_ROOT_CONVERT)) {
            ret = PATTERN_INTERNAL_ROOT_CONVERT.matcher(ret).replaceFirst(StoragePath.INTERNAL_ROOT);
        } else if (ret.startsWith(StoragePath.ROOT_CONVERT)) {
            ret = PATTERN_ROOT_CONVERT.matcher(ret).replaceFirst(StoragePath.ROOT);
        }
        return ret;
    }

    public static String getFilePathFromUri(Context context, Uri uri, boolean isCompressFileFromExtenalApp) {
        String ret = null;
        final String scheme = uri.getScheme();
        if (uri.getAuthority().equals(FILE_PROVIDER_AUTHORITY)) {
            ret = convertDirPathFromUri(uri);
        } else {
            if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
                try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
                    if (cursor == null || cursor.getCount() <= 0 || !cursor.moveToFirst()) {
                        return null;
                    }
                    int cloudIsCachedColumn = cursor.getColumnIndex(GALLERY_COLUMN_CLOUD_IS_CACHE);
                    int existCloudCache = -1;
                    if (cloudIsCachedColumn != -1) {
                        existCloudCache = Integer.parseInt(cursor.getString(cloudIsCachedColumn));
                    }

                    int dataColIndex = cursor.getColumnIndex(MediaColumns.DATA);
                    if (dataColIndex != -1 && existCloudCache != 0) {
                        return cursor.getString(dataColIndex);
                    }
                    int dispNameColIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
                    if (dispNameColIndex == -1) {
                        return null;
                    }
                    String fileName = cursor.getString(dispNameColIndex);
                    if (fileName == null) {
                        return null;
                    }
                    File file = getUniqueFile(fileName);
                    if (isCompressFileFromExtenalApp) {
                        file.createNewFile();
                    } else {
                        createFileFromUri(context, uri, file);
                    }

                    if (file != null) {
                        ret = file.getAbsolutePath();
                    }
                } catch (SecurityException e) {
                    Log.e(TAG, "SecurityException:" + e.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
                ret = uri.getPath();
            }
        }
        return ret;
    }

    public static int getRecordType(ArrayList<FileRecord> items) {
        int recordType = ITEM_TYPE.NONE;
        if (items == null) {
            return recordType;
        }
        for (FileRecord item : items) {
            if (item != null) {
                if (item.isDirectory()) {
                    if (item.getStorageType() == StorageType.Category) {
                        recordType |= ITEM_TYPE.CATEGORY_FOLDER;
                    } else {
                        recordType |= ITEM_TYPE.FOLDER;
                    }
                } else {
                    recordType |= ITEM_TYPE.FILE;
                }
                if (recordType == ITEM_TYPE.FILE_AND_FOLDER) {
                    break;
                }
            }
        }
        return recordType;
    }

    public static boolean hasPrivateFile(ArrayList<FileRecord> items) {
        String path;
        for (FileRecord fileRecord : items) {
            if (fileRecord != null) {
                path = fileRecord.getPath();
                if (!TextUtils.isEmpty(path) && path.startsWith(StoragePath.PRIVATE_ROOT)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isSameAsRealFile(FileRecord record) {
        boolean bRet = false;

        File file = SemFwWrapper.file(record.getFullPath());

        // The size of record and file can be different when file is DRM file
        // and the date also can be different when it is shared via Bluetooth,
        // it has own modified data and it is not shared date.

        if (file.exists()) {
            bRet = true;
        }
        return bRet;
    }

    public static boolean isValidFile(Context context, FileRecord record) {
        boolean bRet = false;

        if (record != null && !TextUtils.isEmpty(record.getFullPath())) {
            bRet = isSameAsRealFile(record);
        }

        if (!bRet) {
            UiUtils.showToast(context, R.string.file_does_not_exist);
        }

        return bRet;
    }

    public static boolean isNoMediaFile(String path) {
        File file = SemFwWrapper.file(path);
        if (file.isDirectory()) {
            return false;
        }

        // special case certain file names
        // I use regionMatches() instead of substring() below
        // to avoid memory allocation
        int lastSlash = path.lastIndexOf('/');
        if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
            // ignore those ._* files created by MacOS
            if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
                return true;
            }

            // ignore album art files created by Windows Media Player:
            // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
            // and AlbumArt_{...}_Small.jpg
            if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
                if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
                        path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
                    return true;
                }
                int length = path.length() - lastSlash - 1;
                if ((length == 17 && path.regionMatches(
                        true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
                        (length == 10
                                && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
                    return true;
                }
            }
        }
        return false;
    }

    public static String getClipDataFilePath(Context context, ClipData.Item item) {
        String ret = null;
        if (item != null) {
            Uri uri = item.getUri();
            if (uri != null) {
                ret = getFilePathFromUri(context, uri, false);
            } else {
                CharSequence uriText = item.getText();
                if (uriText != null) {
                    ret = uriText.toString();
                }
            }
        }
        return ret;
    }

    public static boolean isUnderPath(String parentPath, String path) {
        boolean bRet = false;
        if (path != null && parentPath != null) {
            bRet = path.startsWith(parentPath);
            if (bRet && parentPath.length() != path.length()) {
                bRet = path.charAt(parentPath.length()) == '/';
            }
        }
        return bRet;
    }

    public static int isAddedToSuggestion(Context context, String fullPath) {
        int id = 0;
        DbTableInfo tableInfo = DbTableInfo.getInstance(StorageType.Suggestion);
        if (tableInfo != null) {
            String selection = tableInfo.getColumnName(COLUMN_ID.PATH) + "=?";
            String[] selectionArgs = new String[]{
                    fullPath};
            Uri uri = Uri.parse(tableInfo.getUri());

            try (Cursor c = context.getContentResolver().query(uri, new String[]{"_id"}, selection, selectionArgs, null)) {
                if (c != null && c.moveToFirst()) {
                    id = c.getInt(0);
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception:" + e.toString());
            }
        }
        return id;
    }

    public static boolean removeSuggestionContent(Context context, FileRecord record) {
        boolean bRet = false;
        ContentResolver cr = context.getContentResolver();
        DbTableInfo tableInfo = DbTableInfo.getInstance(StorageType.Suggestion);
        if (tableInfo != null) {
            StringBuilder selection = new StringBuilder();
            selection.append(tableInfo.getColumnName(COLUMN_ID.PATH)).append("=?");
            int row = cr.delete(Uri.parse(tableInfo.getUri()), selection.toString(), new String[]{
                    record.getFullPath()});

            if (row > 0) {
                bRet = true;
            }
        }
        return bRet;
    }

    public static boolean isEmptyStorage(NavigationInfo navigationInfo, AbsMyFilesFragment fragment) {
        boolean bRet = false;
        if (navigationInfo != null) {
            FileRecord record = navigationInfo.getCurRecord();
            if (record != null && record.isRoot() && record.getStorageType() != StorageType.Home) {
                bRet = fragment.getTotalFileCount() == 0;
            }
        }
        return bRet;
    }

    private static FileRecord.CategoryType getCategoryType(File file) {
        FileRecord.CategoryType fileCategoryType = FileRecord.CategoryType.None;

        int fileType = MediaFile.getFileType(file.getAbsolutePath());
        if (FileType.isImageFileType(fileType)) {
            fileCategoryType = FileRecord.CategoryType.Image;
        } else if (FileType.isAudioFileType(fileType)) {
            fileCategoryType = FileRecord.CategoryType.Audio;
        } else if (FileType.isVideoFileType(fileType)) {
            fileCategoryType = FileRecord.CategoryType.Video;
        } else if (FileType.isDocumentFileType(fileType)) {
            fileCategoryType = FileRecord.CategoryType.Document;
        } else if (FileType.isApkFileType(fileType)) {
            fileCategoryType = FileRecord.CategoryType.Apk;
        }

        return fileCategoryType;
    }

    public static ArrayList<FileRecord> getCategoryFolderRecordList(FileOperator operator, ArrayList<FileRecord> recordList) {
        ArrayList<FileRecord> ret;
        ArrayList<FileRecord> categoryFolderRecordList = new ArrayList<>();
        boolean cancel = false;
        for (FileRecord record : recordList) {
            File parent = SemFwWrapper.file(record.getFullPath());
            File[] files = parent.listFiles();
            ArrayList<FileRecord> tempRecordList = new ArrayList<>();
            boolean isAllSameCategoryType = true;
            if (files != null) {
                for (File file : files) {
                    if (operator != null && operator.isOperationCancelled()) {
                        cancel = true;
                        break;
                    }
                    if (!((CategoryFileRecord) record).getCategoryType().equals(FileUtils.getCategoryType(file))) {
                        isAllSameCategoryType = false;
                        continue;
                    }
                    FileRecord innerRecord = new LocalFileRecord(file.getAbsolutePath());
                    tempRecordList.add(innerRecord);
                }
            }
            if (isAllSameCategoryType) {
                categoryFolderRecordList.add(record);
            } else {
                for (FileRecord tempRecord : tempRecordList) {
                    categoryFolderRecordList.add(tempRecord);
                }
            }
        }
        ret = categoryFolderRecordList;
        if (cancel) {
            ret = recordList;
        }
        return ret;
    }

    public static boolean isUnderHiddenFolder(String folderPath) {
        boolean bRet = false;
        if (folderPath != null) {
            String subString[] = folderPath.split("/");
            if (subString.length > 0) {
                for (String sub : subString) {
                    if (sub != null) {
                        bRet = sub.startsWith(".");
                        if (bRet) {
                            break;
                        }
                    }
                }
            }
        }
        return bRet;
    }

    public static String getUserInfoFromUri(Uri uri) {
        String ret;
        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            ret = uri.getUserInfo();
        } else {
            ret = uri.getPath();
            if (ret.startsWith("/storage/emulated/")) {
                ret = ret.replace("/storage/emulated/", "");
                ret = ret.substring(0, ret.indexOf('/'));
            }
        }
        return ret;
    }
}
